#include "track.h"

// ==================================== EDGE BUILDER =================================================
EdgeBuilder::EdgeBuilder()
{ 
	setDefaultValues();
}

EdgeBuilder::~EdgeBuilder()
{ }

void EdgeBuilder::setDefaultValues()
{ 
	a_fixtureDef.shape       = &a_shape;
	a_fixtureDef.friction    = 0.4f;
	a_fixtureDef.density     = 0.0f;
	a_fixtureDef.restitution = 0.0f;
}

void EdgeBuilder::extendSegmentVectorWith(b2Vec2 translation)
{ 
	if (a_segmentVector.empty())
	{
		beginNewSegmentFrom(translation);
	}
	else
	{
		b2Vec2 last_point = a_segmentVector.back();
		b2Vec2 new_translation;
		new_translation.x = last_point.x + translation.x;
		new_translation.y = last_point.y + translation.y;
		a_segmentVector.push_back(new_translation);
	}
}

void EdgeBuilder::beginNewSegmentFrom(b2Vec2 point)
{ 
	clearSegmentVector();
	a_segmentVector.push_back(point);
}

void EdgeBuilder::clearSegmentVector()
{ 
	a_segmentVector.clear();
}

void EdgeBuilder::applySegmentTo(b2Body* p_body)
{ 
	for (unsigned int translation_index = 0; 
		 translation_index < a_segmentVector.size();
		 translation_index++)
	{
		if (translation_index + 1 != a_segmentVector.size())
		{
			a_shape.Set(a_segmentVector[translation_index], a_segmentVector[translation_index+1]);
			p_body->CreateFixture(&a_fixtureDef);
		}
	}
	a_lastAppliedPoint = a_segmentVector.back();
}

b2Vec2 EdgeBuilder::getLastAppliedPoint()
{
	return a_lastAppliedPoint;
}

// ======================================= TRACK =====================================================

Track::Track()
{ }

Track::Track(b2World* p_gameWorld, b2Vec2 position)
{ 
	connectToGameWorld(p_gameWorld);
	buildTrack(position);
	disconnectFromGameWorld();
}

Track::~Track()
{ }

void Track::connectToGameWorld(b2World* p_gameWorld)
{
	ap_gameWorld = p_gameWorld;
	ap_gameWorld->SetContactListener(this);
}

void Track::buildTrack(b2Vec2 position)
{
	a_lastPoint = position;
	b2BodyDef ground_body_definition;
	ap_groundBody = ap_gameWorld->CreateBody(&ground_body_definition);
	biuld_1st_stage();
	biuld_pipe_to_second_stage();
	biuld_2nd_stage();
}

void Track::biuld_1st_stage()
{
	b2Vec2 startig_point(a_lastPoint.x, a_lastPoint.y + 6.0f);
	b2Vec2 translation(0.0f, 0.0f);
	a_edgeBuilder.beginNewSegmentFrom(startig_point);
	translation.Set(  0.0f ,  -6.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( 20.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( 10.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( 10.0f ,   5.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( 10.0f , -10.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( 10.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	a_edgeBuilder.applySegmentTo(ap_groundBody);
	a_lastPoint = a_edgeBuilder.getLastAppliedPoint();
}

void Track::biuld_pipe_to_second_stage()
{
	b2Vec2 startig_point = a_lastPoint;
	b2Vec2 translation   = b2Vec2(0.0f, 0.0f);
	a_edgeBuilder.beginNewSegmentFrom(startig_point);
	translation.Set( 10.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(  0.0f , -10.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(-80.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(  0.0f , -10.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	a_edgeBuilder.applySegmentTo(ap_groundBody);
	startig_point.Set(a_lastPoint.x, a_lastPoint.y + 2.0f);
	a_edgeBuilder.beginNewSegmentFrom(startig_point);
	translation.Set( 12.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(  0.0f , -14.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(-80.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(  0.0f ,  -8.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	a_edgeBuilder.applySegmentTo(ap_groundBody);
	a_lastPoint = a_edgeBuilder.getLastAppliedPoint();
}

void Track::biuld_2nd_stage()
{
	b2Vec2 starting_point(a_lastPoint.x + 2.0f, a_lastPoint.y - 2.0f);
	b2Vec2 translation;
	a_edgeBuilder.beginNewSegmentFrom(starting_point);
	translation.Set(  0.0f ,  -6.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(-10.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(-10.0f , -10.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(-10.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set(  0.0f ,  -2.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	a_edgeBuilder.applySegmentTo(ap_groundBody);
	a_lastPoint = a_edgeBuilder.getLastAppliedPoint();
	
	b2Body* p_trampoline_base;
	b2BodyDef trampoline_base_definition;
	trampoline_base_definition.position.Set(a_lastPoint.x - 2.1f, a_lastPoint.y - 5.0f);
	p_trampoline_base = ap_gameWorld->CreateBody(&trampoline_base_definition);
	b2PolygonShape trampoline_base_box;
	trampoline_base_box.SetAsBox(1.0f, 2.0f);
	b2FixtureDef trampoline_base_fixture;
	trampoline_base_fixture.shape = &trampoline_base_box;
	p_trampoline_base->CreateFixture(&trampoline_base_fixture);
	
	b2BodyDef trampoline_definition;
	trampoline_definition.type = b2_dynamicBody;
	trampoline_definition.position.Set(a_lastPoint.x - 2.1f, a_lastPoint.y - 3.5f);
	ap_trampoline = ap_gameWorld->CreateBody(&trampoline_definition);
	b2PolygonShape trampoline_box;
	trampoline_box.SetAsBox(1.0f, 3.0f);
	b2FixtureDef trampoline_fixture;
	trampoline_fixture.shape = &trampoline_box;
	ap_trampoline->CreateFixture(&trampoline_fixture);
	trampoline_box.SetAsBox(2.0f, 1.0f, b2Vec2(0.0f, 4.0f), 0.0f);
	ap_trampoline->CreateFixture(&trampoline_box, 1.0f);

	b2PrismaticJointDef trampoline_joint_def;
	b2Vec2 world_axis(0.0f, 1.0f);
	trampoline_joint_def.Initialize(p_trampoline_base, ap_trampoline, p_trampoline_base->GetWorldCenter(), world_axis);
	trampoline_joint_def.lowerTranslation = 0.0f;
	trampoline_joint_def.upperTranslation = 5.5f;
	trampoline_joint_def.enableLimit = true;
	trampoline_joint_def.maxMotorForce = 100.0f;
	trampoline_joint_def.enableMotor = false;
	ap_gameWorld->CreateJoint(&trampoline_joint_def);
	ap_trampolineJoint = (b2PrismaticJoint*)ap_gameWorld->CreateJoint(&trampoline_joint_def);

	translation.Set( -4.2f ,   0.0f );
	starting_point = a_edgeBuilder.getLastAppliedPoint() + translation;
	a_edgeBuilder.beginNewSegmentFrom(starting_point);
	translation.Set(  0.0f ,   6.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( -8.0f ,  -8.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	translation.Set( -6.0f ,   0.0f ); a_edgeBuilder.extendSegmentVectorWith(translation);
	a_edgeBuilder.applySegmentTo(ap_groundBody);
	a_lastPoint = a_edgeBuilder.getLastAppliedPoint();
}

void Track::extendTrackSegmentWith(b2Vec2 translation, std::vector<b2Vec2>& r_trackSegment)
{
	if (r_trackSegment.empty())
	{
		r_trackSegment.push_back(translation);
	}
	else
	{
		b2Vec2 last_point = r_trackSegment.back();
		b2Vec2 new_translation;
		new_translation.x = last_point.x + translation.x;
		new_translation.y = last_point.y + translation.y;
		r_trackSegment.push_back(new_translation);
	}
}

void Track::disconnectFromGameWorld()
{
	ap_gameWorld = NULL;
}

// ================================ GRAVITY SENSORS =================================

void Track::BeginContact(b2Contact* contact)
{
		b2Fixture* fixtureA = contact->GetFixtureA();
		b2Fixture* fixtureB = contact->GetFixtureB();
		if (fixtureA->GetBody() == ap_trampoline || fixtureB->GetBody() == ap_trampoline)
		{
			ap_trampolineJoint->SetMotorSpeed(100.0f);
			ap_trampolineJoint->EnableMotor(true);
		}
			/*
		if (fixtureA->GetBody() == ap_backToStartElevator)
			fixtureB->GetBody()->ApplyForceToCenter(b2Vec2(-80.0f, 180.0f));
		if (fixtureB->GetBody() == ap_backToStartElevator)
			fixtureA->GetBody()->ApplyForceToCenter(b2Vec2(-80.0f, 180.0f));

		if (fixtureA->GetBody() == ap_gravitySensor1)
			fixtureB->GetBody()->GetWorld()->SetGravity(b2Vec2(-5.0f, -10.0f));
		if (fixtureB->GetBody() == ap_gravitySensor1)
			fixtureA->GetBody()->GetWorld()->SetGravity(b2Vec2(-5.0f, -10.0f));

		if (fixtureA->GetBody() == ap_gravitySensor2)
			fixtureB->GetBody()->GetWorld()->SetGravity(b2Vec2(-10.0f, 5.0f));
		if (fixtureB->GetBody() == ap_gravitySensor2)
			fixtureA->GetBody()->GetWorld()->SetGravity(b2Vec2(-10.0f, 5.0f));

		if (fixtureA->GetBody() == ap_gravitySensor3)
			fixtureB->GetBody()->GetWorld()->SetGravity(b2Vec2(0.0f, 10.0f));
		if (fixtureB->GetBody() == ap_gravitySensor3)
			fixtureA->GetBody()->GetWorld()->SetGravity(b2Vec2(0.0f, 10.0f));
*/
}

void Track::EndContact(b2Contact* contact)
{
		b2Fixture* fixtureA = contact->GetFixtureA();
		b2Fixture* fixtureB = contact->GetFixtureB();
		if (fixtureA->GetBody() == ap_trampoline || fixtureB->GetBody() == ap_trampoline)
			ap_trampolineJoint->EnableMotor(false);

}



	/*	
	EXAMPLE
	//gravity sensor1 
	b2BodyDef gravity_sensor_definition;
	gravity_sensor_definition.position.Set(
		           ap_gravityCircle->GetPosition().x,
				   ap_gravityCircle->GetPosition().y + 15.0f);
	ap_gravitySensor1 = ap_gameWorld->CreateBody(&gravity_sensor_definition);
	b2PolygonShape gravity_sensor_box;
	gravity_sensor_box.SetAsBox(0.5f, 0.5f);
	b2FixtureDef gravity_sensor_fixture;
	gravity_sensor_fixture.shape = &gravity_sensor_box;
	gravity_sensor_fixture.isSensor = true;
	ap_gravitySensor1->CreateFixture(&gravity_sensor_fixture);

	//gravity sensor2
	gravity_sensor_definition.position.Set(
		           ap_gravityCircle->GetPosition().x + 15.0f,
				   ap_gravityCircle->GetPosition().y);
	ap_gravitySensor2 = ap_gameWorld->CreateBody(&gravity_sensor_definition);
	gravity_sensor_box.SetAsBox(0.5f, 0.5f);
	gravity_sensor_fixture.shape = &gravity_sensor_box;
	gravity_sensor_fixture.isSensor = true;
	ap_gravitySensor2->CreateFixture(&gravity_sensor_fixture);

	//gravity sensor3
	gravity_sensor_definition.position.Set(
		           ap_gravityCircle->GetPosition().x,
				   ap_gravityCircle->GetPosition().y - 15.0f);
	ap_gravitySensor3 = ap_gameWorld->CreateBody(&gravity_sensor_definition);
	gravity_sensor_box.SetAsBox(0.5f, 0.5f);
	gravity_sensor_fixture.shape = &gravity_sensor_box;
	gravity_sensor_fixture.isSensor = true;
	ap_gravitySensor3->CreateFixture(&gravity_sensor_fixture);

	//backToStartElevator
	b2BodyDef elevator_body_definition;
	elevator_body_definition.position.Set(last_point.x + 1.85f, last_point.y);
	ap_backToStartElevator = ap_gameWorld->CreateBody(&elevator_body_definition);
	b2PolygonShape elevator_box;
	elevator_box.SetAsBox(1.7f, 0.8f);
	b2FixtureDef elevator_fixture_definition;
	elevator_fixture_definition.shape = &elevator_box;
	elevator_fixture_definition.isSensor = true;
	ap_backToStartElevator->CreateFixture(&elevator_fixture_definition);
	*/